home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
QRZ! Ham Radio 6
/
QRZ Ham Radio Callsign Database - Volume 6.iso
/
mac
/
files
/
amiga
/
rhinosrc.lha
/
alloc.c
next >
Wrap
C/C++ Source or Header
|
1993-07-04
|
15KB
|
654 lines
/* memory allocation routines
* Copyright 1991 Phil Karn, KA9Q
*
* Adapted from alloc routine in K&R; memory statistics and interrupt
* protection added for use with net package. Must be used in place of
* standard Turbo-C library routines because the latter check for stack/heap
* collisions. This causes erroneous failures because process stacks are
* allocated off the heap.
*/
#include <stdio.h>
#ifdef MSDOS
#include <dos.h>
#include <alloc.h>
#endif
#include "global.h"
#include "mbuf.h"
#include "proc.h"
#include "cmdparse.h"
#ifdef AMIGA
#define LIBRARIES_MATHFFP_H /* Don't include this one! */
#include <exec/lists.h>
#include <exec/memory.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#ifdef __SASC
#include <pragmas/exec_sysbase_pragmas.h>
#endif
extern struct ExecBase *SysBase;
#endif
static unsigned long Memfail; /* Count of allocation failures */
static unsigned long Allocs; /* Total allocations */
static unsigned long Frees; /* Total frees */
static unsigned long Invalid; /* Total calls to free with garbage arg */
static unsigned long Intalloc; /* Calls to malloc with ints disabled */
static unsigned long Intfree; /* Calls to free with ints disabled */
static int Memwait; /* Number of tasks waiting for memory */
static unsigned long Yellows; /* Yellow alert garbage collections */
static unsigned long Reds; /* Red alert garbage collections */
unsigned long Availmem; /* Heap memory, ABLKSIZE units */
static unsigned long Morecores;
static unsigned long Sizes[16];
static int dostat __ARGS((int argc,char *argv[],void *p));
static int dofreelist __ARGS((int argc,char *argv[],void *p));
static int doibufsize __ARGS((int argc,char *argv[],void *p));
static int donibufs __ARGS((int argc,char *argv[],void *p));
static int dothresh __ARGS((int argc,char *argv[],void *p));
static int dosizes __ARGS((int argc,char *argv[],void *p));
int domem(int argc, char *argv[], void *p);
void gcollect(int i, void *v1, void *v2);
struct cmds Memcmds[] = {
"freelist", dofreelist, 0, 0, NULLCHAR,
"ibufsize", doibufsize, 0, 0, NULLCHAR,
"nibufs", donibufs, 0, 0, NULLCHAR,
"sizes", dosizes, 0, 0, NULLCHAR,
"status", dostat, 0, 0, NULLCHAR,
"thresh", dothresh, 0, 0, NULLCHAR,
NULLCHAR,
};
#if defined(MSDOS) && defined(LARGEDATA)
#define HUGE huge
#else
#define HUGE
#endif
union header {
struct {
union header HUGE *ptr;
unsigned long size;
} s;
long l[2];
};
typedef union header HEADER;
#define NULLHDR (HEADER HUGE *)NULL
#define ABLKSIZE (sizeof (HEADER))
static HEADER HUGE *morecore __ARGS((unsigned nu));
static HEADER Base;
static HEADER HUGE *Allocp = NULLHDR;
static unsigned long Heapsize;
#ifdef MSDOS
/* Memory blocks obtained from MS-DOS by allocmem() call */
struct sysblock {
unsigned seg;
unsigned npar;
};
#define NSYSBLOCK 5
struct sysblock Sysblock[NSYSBLOCK];
#endif
#ifdef AMIGA
struct MemBlock {
struct MinNode Node;
long Size;
long pad; /* size multiple of 8==ABLKSIZE */
};
struct MinList SysBlocks;
static void freeblocks(void);
static int Delayfree; /* freeblocks() is slow - don't do it too often */
#define ALLOCMEMSIZE 8192
void *
NextNode(void *n)
{
struct Node *nn;
nn = ((struct Node *)n)->ln_Succ;
if (nn->ln_Pred != n)
log(-1, "List inconsistent: pred(%x) == %x != %x\n", nn,
nn->ln_Pred, n);
if (nn->ln_Succ)
return nn;
return NULL;
}
#endif
/* Allocate block of 'nb' bytes */
void *
malloc(nb)
unsigned nb;
{
register HEADER HUGE *p, HUGE *q;
register unsigned nu;
int i;
if(!istate())
Intalloc++;
if(nb == 0)
return NULL;
/* Record the size of this request */
if((i = log2(nb)) >= 0)
Sizes[i]++;
/* Round up to full block, then add one for header */
nu = ((nb + ABLKSIZE - 1) / ABLKSIZE) + 1;
if((q = Allocp) == NULLHDR){
Base.s.ptr = Allocp = q = &Base;
Base.s.size = 1;
#ifdef AMIGA
NewList((struct List *)&SysBlocks);
#endif
}
for(p = q->s.ptr; ; q = p, p = p->s.ptr){
if(p->s.size >= nu){
/* This chunk is at least as large as we need */
if(p->s.size <= nu + 1){
/* This is either a perfect fit (size == nu)
* or the free chunk is just one unit larger.
* In either case, alloc the whole thing,
* because there's no point in keeping a free
* block only large enough to hold the header.
*/
q->s.ptr = p->s.ptr;
} else {
/* Carve out piece from end of entry */
p->s.size -= nu;
p += p->s.size;
p->s.size = nu;
}
#ifdef circular
Allocp = q;
#endif
p->s.ptr = p; /* for auditing */
Allocs++;
Availmem -= p->s.size;
p++;
break;
}
if(p == Allocp && ((p = morecore(nu)) == NULLHDR)){
Memfail++;
break;
}
}
#if defined(LARGEDATA) && defined(MSDOS)
/* On the brain-damaged Intel CPUs in "large data" model,
* make sure the pointer's offset field isn't null
* (unless the entire pointer is null).
* The Turbo C compiler and certain
* library functions like strrchr() assume this.
*/
if(FP_OFF(p) == 0 && FP_SEG(p) != 0){
/* Return denormalized but equivalent pointer */
return (void *)MK_FP(FP_SEG(p)-1,16);
}
#endif
return (void *)p;
}
/* Get more memory from the system and put it on the heap */
static HEADER HUGE *
morecore(nu)
unsigned nu;
{
#ifdef MSDOS
char HUGE *cp;
HEADER HUGE *up;
unsigned size;
unsigned segp;
unsigned npar;
struct sysblock *sp;
int i;
Morecores++;
size = nu * ABLKSIZE;
/* First try to expand our main memory block */
if ((int)(cp = (char HUGE *)sbrk(size)) != -1){
up = (HEADER *)cp;
up->s.size = nu;
up->s.ptr = up; /* satisfy audit */
free((void *)(up + 1));
Heapsize += size;
Frees--; /* Nullify increment inside free() */
return Allocp;
}
/* That failed; the main memory block must have grown into another
* allocated block, or something else (e.g., the increase handles
* call in ioinit()) must have allocated memory just beyond it.
* Allocate or extend an additional memory block.
*/
npar = (size+16)/16; /* Convert size from bytes to paragraphs */
cp = NULL;
for(sp=Sysblock,i=0;i < NSYSBLOCK;i++,sp++){
if(sp->npar != 0){
/* Try to expand this block */
if(setblock(sp->seg,sp->npar + npar) != -1){
/* Failed (-1 == SUCCESS; strange!) */
continue;
}
/* Block expansion succeeded */
cp = MK_FP(sp->seg + sp->npar,0);
sp->npar += npar;
} else {
/* Allocate new block */
if(allocmem(npar,&segp) != -1){
return NULL; /* Complete failure */
}
/* -1 indicates SUCCESS (strange) */
sp->seg = segp;
sp->npar = npar;
cp = MK_FP(segp,0);
}
break;
}
#endif
#ifdef AMIGA
char *cp;
HEADER *up;
unsigned long size;
unsigned npar;
Morecores++;
size = nu * ABLKSIZE;
/* add MemBlock and make a full nr of intel paragraphs */
size = (size + sizeof(struct MemBlock) + 15) & ~15;
size = max(size, ALLOCMEMSIZE); /* blocks of 8K or more */
cp = AllocMem(size, MEMF_PUBLIC);
if (cp) {
((struct MemBlock *)cp)->Size = size;
AddTail((struct List *)&SysBlocks, (struct Node *)cp);
cp += sizeof(struct MemBlock);
npar = (size-sizeof(struct MemBlock))/16; /* Convert size from bytes to paragraphs */
Delayfree++;
}
#endif
if(cp != (char HUGE *)NULL){
/* Expand or create succeeded, add to heap */
up = (HEADER *)cp;
up->s.size = (npar*16)/ABLKSIZE;
up->s.ptr = up; /* satisfy audit */
free((void *)(up + 1));
Heapsize += npar*16;
Frees--; /* Nullify increment inside free() */
return Allocp;
}
return NULL;
}
/* Put memory block back on heap */
void
free(blk)
void *blk;
{
register HEADER HUGE *p, HUGE *q;
unsigned short HUGE *ptr;
if(!istate())
Intfree++;
if(blk == NULL)
return; /* Required by ANSI */
p = (HEADER HUGE *)blk - 1;
/* Audit check */
if(p->s.ptr != p){
printf("free: WARNING! invalid pointer (0x%lx) proc %s\n",
ptol(blk),Curproc->name);
stktrace();
Invalid++;
#ifdef MSDOS
ptr = (unsigned short *)&blk;
log(-1,"free: WARNING! invalid pointer (0x%lx) pc = 0x%x %x proc %s\n",
ptol(blk),ptr[-1],ptr[-2],Curproc->name);
#endif
#ifdef AMIGA
/* Sorry, this is probably very SAS/C 6.x specific...
* Stack layout:
* <return addr>
* <auto variable(s): ptr>
* <saved registers>
* Note there is no frame pointer, and no pushed args.
*/
ptr = (unsigned short *)&ptr;
log(-1,"free: WARNING! invalid pointer (0x%lx) pc = 0x%06x proc %s\n",
blk,((unsigned long *)ptr)[1],Curproc->name);
#endif
return;
}
Availmem += p->s.size;
/* Search the free list looking for the right place to insert */
for(q = Allocp; !(p > q && p < q->s.ptr); q = q->s.ptr){
/* Highest address on circular list? */
if(q >= q->s.ptr && (p > q || p < q->s.ptr))
break;
}
if(p + p->s.size == q->s.ptr){
/* Combine with front of this entry */
p->s.size += q->s.ptr->s.size;
p->s.ptr = q->s.ptr->s.ptr;
} else {
/* Link to front of this entry */
p->s.ptr = q->s.ptr;
}
if(q + q->s.size == p){
/* Combine with end of this entry */
q->s.size += p->s.size;
q->s.ptr = p->s.ptr;
} else {
/* Link to end of this entry */
q->s.ptr = p;
}
#ifdef circular
Allocp = q;
#endif
Frees++;
if(Memwait != 0)
psignal(&Memwait,0);
#ifdef AMIGA
else if ((Availmem * (ABLKSIZE / 2)) > Memthresh)
freeblocks();
#endif
}
#ifdef AMIGA
void
freeall(void)
{
struct MemBlock *p;
while (p = (struct MemBlock *)RemHead((struct List *)&SysBlocks))
FreeMem((char *)p, p->Size);
Allocp = NULLHDR;
Availmem = 0;
Heapsize = 0;
}
static void
freeblocks(void)
{
struct MBH { /* Layout of memory region */
struct MemBlock m;
HEADER h;
} *m, *n;
if (--Delayfree >= 0)
return;
Delayfree = 128;
for (m = (struct MBH *)SysBlocks.mlh_Head;
n = (struct MBH *)m->m.Node.mln_Succ; m = n) {
int size;
if (m->h.s.ptr == &m->h) /* Is he really free memory? */
continue;
size = m->h.s.size * ABLKSIZE;
/* Does he go all the way? */
if (m->m.Size == size + sizeof(struct MemBlock)) {
void *v;
v = malloc(size - ABLKSIZE); /* unlink from freelist */
if (v != (m + 1)) {
free(v);
continue;
}
Remove((struct Node *)&m->m.Node);
Heapsize -= size;
Allocs--; /* nullify increment inside malloc() */
FreeMem((char *)m, m->m.Size);
return;
}
}
}
#endif
#ifdef AMIGA /* Not presently used by pc */
/* Move existing block to new area */
void *
realloc(area,size)
void *area;
unsigned size;
{
unsigned osize;
HEADER HUGE *hp;
char HUGE *cp;
hp = ((HEADER *)area) - 1;
osize = (hp->s.size -1) * ABLKSIZE;
free(area);
if((cp = malloc(size)) != NULL && cp != area)
memcpy((char *)cp,(char *)area,size>osize? osize : size);
return cp;
}
#endif
/* Allocate block of cleared memory */
void *
calloc(nelem,size)
unsigned nelem; /* Number of elements */
unsigned size; /* Size of each element */
{
register unsigned i;
register char *cp;
i = nelem * size;
if((cp = malloc(i)) != NULL)
memset(cp,0,i);
return cp;
}
/* Version of malloc() that waits if necessary for memory to become available */
void *
mallocw(nb)
unsigned nb;
{
register void *p;
while((p = malloc(nb)) == NULL){
Memwait++;
pwait(&Memwait);
Memwait--;
}
return p;
}
/* Version of calloc that waits if necessary for memory to become available */
void *
callocw(nelem,size)
unsigned nelem; /* Number of elements */
unsigned size; /* Size of each element */
{
register unsigned i;
register char *cp;
i = nelem * size;
cp = mallocw(i);
memset(cp,0,i);
return cp;
}
/* Return 0 if at least Memthresh memory is available. Return 1 if
* less than Memthresh but more than Memthresh/2 is available; i.e.,
* if a yellow garbage collection should be performed. Return 2 if
* less than Memthresh/2 is available, i.e., a red collection should
* be performed.
*/
int
availmem()
{
void *p;
if(Availmem*ABLKSIZE >= Memthresh)
return 0; /* We're clearly OK */
/* There's not enough on the heap; try calling malloc to see if
* it can get more from the system
*/
if((p = malloc(Memthresh)) != NULL){
free(p);
return 0; /* Okay */
}
if((p = malloc(Memthresh/2)) != NULL){
free(p);
return 1; /* Yellow alert */
}
return 2; /* Red alert */
}
/* Print heap stats */
static int
dostat(argc,argv,envp)
int argc;
char *argv[];
void *envp;
{
struct sysblock *sp;
int i;
printf("heap size %lu avail %lu (%lu%%) morecores %lu\n",
Heapsize,Availmem * ABLKSIZE,100L*Availmem*ABLKSIZE/Heapsize,
Morecores);
#ifdef MSDOS
if(Sysblock[0].npar != 0){
printf("Extra blocks:");
for(i=0,sp=Sysblock;i< NSYSBLOCK;i++,sp++){
if(sp->npar == 0)
break;
printf(" (%x0-%x0)",sp->seg,sp->seg+sp->npar);
}
printf("\n");
}
#endif
#ifdef AMIGA
{
struct MemBlock *m;
printf("Extra blocks:");
m = (struct MemBlock *)&SysBlocks;
while (m = NextNode(m)) {
printf(" (%06x-%06x)", m, (char *)m + m->Size);
}
printf("\n");
}
#endif
printf("allocs %lu frees %lu (diff %lu) alloc fails %lu invalid frees %lu\n",
Allocs,Frees,Allocs-Frees,Memfail,Invalid);
printf("pushdown calls %lu pushdown calls to malloc %lu\n",
Pushdowns,Pushalloc);
printf("interrupts-off calls to malloc %lu free %lu\n",Intalloc,Intfree);
printf("garbage collections yellow %lu red %lu\n",Yellows,Reds);
iqstat();
return 0;
}
/* Print heap free list */
static int
dofreelist(argc,argv,envp)
int argc;
char *argv[];
void *envp;
{
HEADER HUGE *p;
int i = 0;
for(p = Base.s.ptr;p != (HEADER HUGE *)&Base;p = p->s.ptr){
printf("%6lx %6lu",ptol((void *)p),p->s.size * ABLKSIZE);
if(++i == 4){
i = 0;
if(printf("\n") == EOF)
return 0;
} else
printf(" | ");
}
if(i != 0)
printf("\n");
return 0;
}
static int
dosizes(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int i;
for(i=0;i<16;i += 4){
printf("N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld| N>=%5u:%7ld\n",
1<<i,Sizes[i], 2<<i,Sizes[i+1],
4<<i,Sizes[i+2],8<<i,Sizes[i+3]);
}
return 0;
}
int
domem(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return subcmd(Memcmds,argc,argv,p);
}
static int
donibufs(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setint(&Nibufs,"Interrupt pool buffers",argc,argv);
}
static int
doibufsize(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setuns(&Ibufsize,"Interrupt buffer size",argc,argv);
}
static int
dothresh(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setlong(&Memthresh,"Free memory threshold (bytes)",argc,argv);
}
/* Background memory compactor, used when memory runs low */
void
gcollect(i,v1,v2)
int i; /* Args not used */
void *v1;
void *v2;
{
void (**fp)__ARGS((int));
int red;
for(;;){
pause(1000L); /* Run every second */
/* If memory is low, collect some garbage. If memory is VERY
* low, invoke the garbage collection routines in "red" mode.
*/
switch(availmem()){
case 0:
continue; /* All is well */
case 1:
red = 0;
Yellows++;
break;
case 2:
red = 1;
Reds++;
break;
}
for(fp = Gcollect;*fp != NULL;fp++)
(**fp)(red);
}
}